#include "array.h"
#include "pbc.h"
#include "number.h"
#include <stdlib.h>
#include <string.h>


#define  BUFFER_SIZE 128

struct one_node
{
    char name[NAME_SIZE];
    int tagsz;
    uint8_t *buffer;
    uint8_t *ptr;
    int cap;
};

struct repeat_array
{
    struct one_node *node;
    int size;
    int cap;
};


int repeat_array_block()
{
    return sizeof(struct repeat_array);
}

void repeat_array_init(struct repeat_array *array)
{
    array->size = 0;
    array->cap = 4;

    array->node = calloc(sizeof(struct one_node), array->cap);

    int i;
    for (i = 0; i < array->cap; ++i)
    {
        struct one_node *n = &array->node[i];
        n->cap = BUFFER_SIZE;
        n->buffer = malloc(n->cap);
        n->ptr = n->buffer;
    }
}

void repeat_array_free(struct repeat_array *array)
{
    int i;
    for (i = 0; i < array->cap; ++i)
    {
        struct one_node *n = &array->node[i];
        free(n->buffer);
    }

    free(array->node);
}

static struct one_node *find_node_by_name(struct repeat_array *array, const char *name, int *once)
{
    if (array->size == 0)
    {
        *once = 1;
        array->size++;
        strcpy(array->node[0].name, name);
        return &array->node[0];
    }

    int i;
    for (i = 0; i < array->size; ++i)
    {
        struct one_node *n = &array->node[i];
        if (strcmp(n->name, name) == 0)
        {
            *once = 0;
            return n;
        }
    }

    if (array->size > array->cap * 3/4)
    {
        fprintf(stderr, "array need expand more space.\n");
        exit(1);
    }


    *once = 1;
    int idx = array->size++;
    strcpy(array->node[idx].name, name);
    return &array->node[idx];
}

void repeat_array_pushi32(struct repeat_array *array, const char *name, int tag, int value)
{
    int once = 0;
    struct one_node *n = find_node_by_name(array, name, &once);
    if (once == 1)
    {
        once = pbc_encode32(tag, n->ptr);
        n->tagsz = once;
        n->ptr += once;
    }

    n->ptr += pbc_encode32(value, n->ptr);
}


void repeat_array_pushi64(struct repeat_array *array, const char *name, int tag, int64_t value)
{
    int once = 0;
    struct one_node *n = find_node_by_name(array, name, &once);
    if (once == 1)
    {
        once = pbc_encode32(tag, n->ptr);
        n->tagsz = once;
        n->ptr += once;
    }

    n->ptr += pbc_encode64(value, n->ptr);
}

void repeat_array_pushstr(struct repeat_array *array, const char *name, int tag, const char *value)
{
    int once = 0;
    struct one_node *n = find_node_by_name(array, name, &once);
    if (once == 1)
    {
        once = pbc_encode32(tag, n->ptr);
        n->tagsz = once;
        n->ptr += once;
    }

    int len = strlen(value);
    n->ptr += pbc_encode32(len, n->ptr);
    memcpy(n->ptr, value, len);
    n->ptr += len;
}


int repeat_array_size(struct repeat_array *array)
{
    return array->size;
}

// 强制扩容
static struct chumk *force_buffer_expand(int sz, struct chumk *ck)
{
    uint8_t *buf = malloc(sz);
    memcpy(buf, ck->buffer, ck->len);

    free(ck->buffer);

    ck->buffer = buf;
    return ck;
}


struct chumk *combine_wmessage_array(struct repeat_array *array, struct chumk *ck, int *sz)
{
    if (array->size < 0)
        return ck;

    uint8_t *ptr = ck->buffer + ck->len;
    int i, reallen;

    for (i = 0; i < array->size; ++i)
    {
        struct one_node *n = &array->node[i];
        reallen = n->ptr - n->buffer - n->tagsz;

        if ((ck->len + reallen + 4) > *sz)
        {
            *sz = (ck->len + reallen) * 2;
            ck = force_buffer_expand(*sz, ck);
            ptr = ck->buffer + ck->len;
        }

        memcpy(ptr, n->buffer, n->tagsz);
        ptr += n->tagsz;
        ck->len += n->tagsz;

        int ntp = pbc_encode32(reallen, ptr);
        ptr += ntp;
        ck->len += ntp;

        memcpy(ptr, n->buffer + n->tagsz, reallen);
        ck->len += reallen;
        ptr = ck->buffer + ck->len;
    }

    return ck;
}


